/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package com.inspur.edp.lcm.metadata.core.index;

import com.inspur.edp.lcm.metadata.api.entity.MetadataPackage;
import com.inspur.edp.lcm.metadata.api.entity.MetadataProject;
import com.inspur.edp.lcm.metadata.api.entity.ReferenceSourceType;
import com.inspur.edp.lcm.metadata.api.mvnEntity.MavenPackageRefs;
import com.inspur.edp.lcm.metadata.common.FileServiceImp;
import com.inspur.edp.lcm.metadata.common.Utils;
import com.inspur.edp.lcm.metadata.core.MetadataCoreManager;
import com.inspur.edp.lcm.metadata.core.MetadataProjectCoreService;
import com.inspur.edp.lcm.metadata.core.entity.MdProjectDependencyNode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.apache.maven.model.Model;
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * maven/package 依赖元数据工程缓存基类
 *
 * @author sunhongfei01
 * @date modify by 2021-8-2
 */
@Slf4j
public abstract class MetadataPackageIndexService {
    // protected final static String KEY_CONNECTOR = "_";
    // protected final static String FEATURE_IN_NAME = "-api-";
    // 依赖maven工程缓存
    protected final static LruCache<String, LruCache<String, MdProjectDependencyNode>> metadataDependencyNodeCache = new LruCache<>(50);
    protected final FileServiceImp fileServiceImp = new FileServiceImp();
    final MetadataCoreManager metadataManager = MetadataCoreManager.getCurrent();
    final MetadataProjectCoreService projectService = MetadataProjectCoreService.getCurrent();

    MdProjectDependencyNode getNodeFromCache(String location, String indexKey) {
        LruCache<String, MdProjectDependencyNode> mavenCache = metadataDependencyNodeCache.get(location);
        if (mavenCache == null) {
            mavenCache = new LruCache<>(3000);
            metadataDependencyNodeCache.add(location, mavenCache);
        }
        return mavenCache.get(indexKey);
    }

    void removeNodeFromCache(String location, String indexKey) {
        LruCache<String, MdProjectDependencyNode> mavenCache = metadataDependencyNodeCache.get(location);
        if (mavenCache == null) {
            mavenCache = new LruCache<>(3000);
            metadataDependencyNodeCache.add(location, mavenCache);
        }
        mavenCache.remove(indexKey);
    }

    void saveNodeToCache(String location, String indexKey, MdProjectDependencyNode node) {
        LruCache<String, MdProjectDependencyNode> mavenCache = metadataDependencyNodeCache.get(location);
        if (mavenCache == null) {
            mavenCache = new LruCache<>(3000);
            metadataDependencyNodeCache.add(location, mavenCache);
        }
        mavenCache.add(indexKey, node);
    }

    // 获取元数据工程 依赖节点
    public static MdProjectDependencyNode getDependencyNodeByProject(String projectPath, MetadataProject metadataProject) {
        return MetadataPackageIndexServiceForProject.getInstance().getMetadataDependencyNode(projectPath, metadataProject);
    }
    // 获取元数据工程 依赖节点
    public static MdProjectDependencyNode getDependencyNodeByProject(String projectPath) {
        return getDependencyNodeByProject(projectPath, null);
    }

    // 获取Maven引用元数据包 依赖节点
    public static MdProjectDependencyNode getDependencyNodeByMaven(MavenPackageRefs mavenRef) {
        return MetadataPackageIndexServiceForMaven.getInstance().getMetadataDependencyNode(mavenRef);
    }

    public static MdProjectDependencyNode getDependencyNodeByPackage(String metadataPackageName) {
        return MetadataPackageIndexServiceForPackages.getInstance().getMetadataDependencyNode(metadataPackageName);
    }

    public static MdProjectDependencyNode getDependencyNodeByDB(String metadataPackageName) {
        return MetadataPackageIndexServiceForDB.getInstance().getMetadataDependencyNode(metadataPackageName);
    }

    /**
     * 根据文件夹目录 获取其下的依赖元数据工程信息
     *
     * @param dirPath 文件夹目录
     * @return 元数据工程依赖节点
     */
    MdProjectDependencyNode getDependencyNodeInfoFromDir(String location, String dirPath, boolean isNeedPom) {
        String dirFullPath = fileServiceImp.getCombinePath(location, dirPath);

        File dir = new File(dirFullPath);
        File[] files = dir.listFiles();
        if (files == null) {
            return null;
        }
        MdProjectDependencyNode dependencyNode = new MdProjectDependencyNode();
        boolean isFind = false;
        //TODO 是否考虑文件夹下存在多个依赖场景
        for (File file : files) {
            if (file.getName().endsWith(Utils.getMetadataPackageExtension())) {
                isFind = true;
                dependencyNode.setMetadataPackageName(FilenameUtils.getBaseName(file.getName()));
                dependencyNode.setLocation(file.getAbsolutePath());
                dependencyNode.setLastModified(file.lastModified());
            }
            if (isNeedPom && file.getName().endsWith(Utils.getPomSuffix())) {
                dependencyNode.setDepFileLocation(file.getAbsolutePath());
            }
        }

        if (!isFind) {
            return null;
        }
        return dependencyNode;
    }

    /**
     * 从元数据包中获取包内元数据编码列表并缓存入元数据包索引中
     *
     * @param metadataPackageInfo 元数据包
     */
    List<String> getMetadataIdsInMdPkg(MetadataPackage metadataPackageInfo) {
        List<String> metadataIds = new ArrayList<>();
        if (metadataPackageInfo != null && metadataPackageInfo.getMetadataList() != null) {
            metadataPackageInfo.getMetadataList().forEach(metadata -> metadataIds.add(metadata.getHeader().getId()));
        }
        return metadataIds;
    }

    /**
     * 获取元数据包中记录的元数据包引用信息
     * @param metadataPackageInfo 当前元数据包
     * @return 元数据包引用元数据包名列表
     */
    Set<String> getMetadataRefsInMdPkg(MetadataPackage metadataPackageInfo) {
        Set<String> metadataRefs = new HashSet<>();
        if (metadataPackageInfo != null && metadataPackageInfo.getReference() != null) {
            metadataRefs = metadataPackageInfo.getReference().stream()
                    .filter(reference -> reference.getReferenceSourceType() != ReferenceSourceType.Mdpkg &&
                            reference.getReferenceSourceType() != ReferenceSourceType.External)
                    .map(reference -> reference.getDepententPackage().getName())
                    .collect(Collectors.toSet());
        }
        return metadataRefs;
    }

    /**
     * 获取元数据包中记录的数据库元数据包引用信息
     * @param metadataPackageInfo 当前元数据包
     * @return 元数据包引用元数据包名列表
     */
    Set<String> getMdpkgRefsInMdPkg(MetadataPackage metadataPackageInfo) {
        Set<String> metadataRefs = new HashSet<>();
        if (metadataPackageInfo != null && metadataPackageInfo.getReference() != null) {
            metadataRefs = metadataPackageInfo.getReference().stream()
                    .filter(reference -> reference.getReferenceSourceType() == ReferenceSourceType.Mdpkg)
                    .map(reference -> reference.getDepententPackage().getName())
                    .collect(Collectors.toSet());
        }
        return metadataRefs;
    }

    /**
     * 获取元数据包中记录的元数据包引用信息
     * @param metadataPackageInfo 当前元数据包
     * @return 元数据包引用元数据包名列表
     */
    Set<String> getIdpMdIdsInMdPkg(MetadataPackage metadataPackageInfo) {
        Set<String> idpMetadataIds = new HashSet<>();
        if (metadataPackageInfo != null && metadataPackageInfo.getReference() != null) {
            idpMetadataIds = metadataPackageInfo.getReference().stream()
                    .filter(reference -> reference.getReferenceSourceType() == ReferenceSourceType.External)
                    .map(reference -> reference.getExternalInfo().getMetadataId())
                    .collect(Collectors.toSet());
        }
        return idpMetadataIds;
    }

    Model getModel(String depFileLocation) {
        try (FileInputStream fis = new FileInputStream(depFileLocation)) {
            MavenXpp3Reader reader = new MavenXpp3Reader();
            return reader.read(fis);
        } catch (IOException | XmlPullParserException e) {
            log.error("读取Maven文件[{}]失败", depFileLocation, e);
        }
        return null;
    }

    protected static class LruCache<K, V> implements Serializable {
        private static final long serialVersionUID = 1L;
        private final Map<K, V> cache;

        public LruCache(final int cacheSize) {
            cache = new LinkedHashMap<K, V>() {
                protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
                    return size() > cacheSize;
                }
            };
        }

        public void add(K key, V value) {
            synchronized(cache) {
                cache.put(key, value);
            }
        }

        public V get(K key) {
            synchronized(cache) {
                return cache.get(key);
            }
        }

        public V remove(K key) {
            synchronized(cache) {
                return cache.remove(key);
            }
        }
    }
}


